/**
 * MOAB, a Mesh-Oriented datABase, is a software component for creating,
 * storing and accessing finite element mesh data.
 *
 * Copyright 2004 Sandia Corporation.  Under the terms of Contract
 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
 * retains certain rights in this software.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 */

// Contributed by Lorenzo Alessio Botti (SpaFEDTe)
// This implementation is mostly borrowed from the mbzoltan MOAB partitioning tool

#ifndef __metispartitioner_hpp__
#define __metispartitioner_hpp__

#include <cstdlib>
#include "moab/PartitionerBase.hpp"
#include "metis.h"

namespace moab
{

class Interface;
class Range;
}  // namespace moab

using namespace moab;

class MetisPartitioner : public PartitionerBase< idx_t >
{

  public:
    MetisPartitioner( Interface* impl = NULL, const bool use_coords = false );

    virtual ~MetisPartitioner();

    virtual ErrorCode partition_mesh_and_geometry( const double part_geom_mesh_size,
                                                   const idx_t nparts,
                                                   const char* zmethod,
                                                   const char* other_method,
                                                   double imbal_tol,
                                                   const int part_dim           = 3,
                                                   const bool write_as_sets     = true,
                                                   const bool write_as_tags     = false,
                                                   const int obj_weight         = 0,
                                                   const int edge_weight        = 0,
                                                   const int projection_type    = 0,
                                                   const bool recompute_rcb_box = false,
                                                   const bool print_time        = false );

    virtual ErrorCode partition_mesh( const idx_t nparts,
                                      const char* method,
                                      const int part_dim               = 3,
                                      const bool write_as_sets         = true,
                                      const bool write_as_tags         = false,
                                      const bool partition_tagged_sets = false,
                                      const bool partition_tagged_ents = false,
                                      const char* aggregating_tag      = NULL,
                                      const bool print_time            = false );

    virtual ErrorCode write_partition( const idx_t nparts,
                                       Range& elems,
                                       const idx_t* assignment,
                                       const bool write_as_sets,
                                       const bool write_as_tags );

    ErrorCode write_aggregationtag_partition( const idx_t nparts,
                                              Range& elems,
                                              const idx_t* assignment,
                                              const bool write_as_sets,
                                              const bool write_as_tags );

    // put closure of entities in the part sets too
    virtual ErrorCode include_closure();

    // virtual ErrorCode write_file(const char *filename, const char *out_file);

  private:
    ErrorCode assemble_graph( const int dimension,
                              std::vector< double >& coords,
                              std::vector< idx_t >& moab_ids,
                              std::vector< idx_t >& adjacencies,
                              std::vector< idx_t >& length,
                              Range& elems );

    ErrorCode assemble_taggedsets_graph( const int dimension,
                                         std::vector< double >& coords,
                                         std::vector< idx_t >& moab_ids,
                                         std::vector< idx_t >& adjacencies,
                                         std::vector< idx_t >& length,
                                         Range& elems,
                                         const char* aggregating_tag );

    ErrorCode assemble_taggedents_graph( const int dimension,
                                         std::vector< double >& coords,
                                         std::vector< idx_t >& moab_ids,
                                         std::vector< idx_t >& adjacencies,
                                         std::vector< idx_t >& length,
                                         Range& elems,
                                         const char* aggregating_tag );
};

// Inline functions

inline ErrorCode MetisPartitioner::partition_mesh_and_geometry( const double,
                                                                const idx_t nparts,
                                                                const char* zmethod,
                                                                const char*,
                                                                double,
                                                                const int part_dim,
                                                                const bool write_as_sets,
                                                                const bool write_as_tags,
                                                                const int,
                                                                const int,
                                                                const int,
                                                                const bool,
                                                                const bool print_time )
{
    // Only partition the mesh - no geometric partition available
    return partition_mesh( nparts, zmethod, part_dim, write_as_sets, write_as_tags, false, false, NULL, print_time );
}

inline ErrorCode MetisPartitioner::include_closure()
{
    return MB_NOT_IMPLEMENTED;
}

#endif
